Skip to content

peripheral: add SCB NSACR and NVIC ITNS support for ARMv8-M TrustZone#647

Open
leftger wants to merge 3 commits intorust-embedded:masterfrom
leftger:feat/trustzone
Open

peripheral: add SCB NSACR and NVIC ITNS support for ARMv8-M TrustZone#647
leftger wants to merge 3 commits intorust-embedded:masterfrom
leftger:feat/trustzone

Conversation

@leftger
Copy link
Copy Markdown

@leftger leftger commented Apr 21, 2026

Motivation

When setting up a TrustZone Secure world on Cortex-M33 (ARMv8-M), three things need to happen before jumping to the Non-Secure application:

  1. Allow Non-Secure code to use the FPU — requires writing SCB->NSACR bits 10–11 (CP10/CP11).
  2. Route peripheral interrupts to the Non-Secure world — requires writing NVIC->ITNS[n].
  3. Optionally query whether a given interrupt is already routed to Non-Secure.

Neither SCB->NSACR nor typed NVIC->ITNS accessors are currently exposed by this crate, forcing users to fall back to raw pointer writes against magic addresses. This PR closes that gap.

Changes

scb.rs

  • Add pub nsacr: RW<u32> to RegisterBlock, gated #[cfg(armv8m)], at the correct offset (CPACR + 4 = 0xE000_ED8C).
  • Add an #[cfg(armv8m)] impl SCB block with three methods:
    • enable_nonsecure_fpu(&mut self) — sets NSACR bits 10–11.
    • disable_nonsecure_fpu(&mut self) — clears them.
    • is_nonsecure_fpu_enabled() -> bool — reads the current state.

nvic.rs

The itns: [RW<u32>; 16] field already exists (added in #580) but had no typed methods. Three #[cfg(armv8m)] methods are added to impl NVIC:

  • unsafe route_to_nonsecure<I: InterruptNumber>(interrupt: I) — sets the ITNS bit.
  • unsafe route_to_secure<I: InterruptNumber>(interrupt: I) — clears it.
  • is_routed_to_nonsecure<I: InterruptNumber>(interrupt: I) -> bool — reads it.

The unsafe on the mutating methods matches the precedent set by unmask — incorrect routing can violate security invariants.

test.rs

  • Address assertions for nvic.itns (0xE000_E380) and nvic.itns[1] (0xE000_E384), gated #[cfg(armv8m)].
  • Address assertion for scb.nsacr (0xE000_ED8C), gated #[cfg(armv8m)].

Testing

cargo test -p cortex-m --features std --lib passes (11/11). The new #[cfg(armv8m)] assertions are verified when targeting thumbv8m.main-none-eabihf. The pre-existing macros::CPASS_ATTR doctest failure is a macOS LLVM section-specifier issue unrelated to this PR.

Add the missing ARMv8-M TrustZone registers and typed helper methods:

- SCB: add `nsacr` field (Non-Secure Access Control Register, 0xE000_ED8C)
  gated behind `#[cfg(armv8m)]`. Add `enable_nonsecure_fpu()`,
  `disable_nonsecure_fpu()`, and `is_nonsecure_fpu_enabled()` helpers
  on SCB for controlling CP10/CP11 (FPU) access from Non-Secure code.

- NVIC: add `route_to_nonsecure<I>()`, `route_to_secure<I>()`, and
  `is_routed_to_nonsecure<I>()` typed methods on NVIC, using the
  existing `itns` register field (already present since rust-embedded#580). Gated
  behind `#[cfg(armv8m)]`.

- test: add address assertions for `nvic.itns` (0xE000_E380) and
  `scb.nsacr` (0xE000_ED8C), both gated `#[cfg(armv8m)]`.
@leftger
Copy link
Copy Markdown
Author

leftger commented Apr 21, 2026

The rt-ci-linux failures are pre-existing on master (same job, same error) and unrelated to this PR. The failure is a stale expected-error string in cortex-m-rt/tests/compile-fail/interrupt-not-reexported.rs — the compiler now emits cannot find module or crate interrupt in this scope [E0433] instead of the expected failed to resolve: use of unresolved module or unlinked crate interrupt``. This only affects the cortex-m-rt compile-fail testsuite, not `cortex-m`.

@leftger
Copy link
Copy Markdown
Author

leftger commented Apr 21, 2026

One downstream motivating use-case for this PR is the embassy-stm32 TrustZone/SAU driver.

Currently route_irq_to_nonsecure and enable_nonsecure_fpu hardcode the NVIC ITNS base address (0xE000_E380) and the SCB NSACR address (0xE000_ED8C) with raw pointer reads/writes, along with TODO comments pointing to this PR. Once this lands, those functions can be cleaned up to use NVIC::route_to_nonsecure() and SCB::enable_nonsecure_fpu() respectively, removing all the manual register offsets.

Comment thread cortex-m/src/peripheral/nvic.rs Outdated
Improves readability of the ARMv8-M route_to_nonsecure, route_to_secure,
and is_routed_to_nonsecure methods by naming the nr/32 and nr%32 sub-
expressions as suggested in PR review.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants